package com.information.manage.basic.audit.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.information.manage.basic.audit.constant.AuditTaskStatusEnum;
import com.information.manage.basic.audit.constant.AuditTemplateStatusEnum;
import com.information.manage.basic.audit.constant.AuditTypeEnum;
import com.information.manage.basic.audit.constant.ShrlxEnum;
import com.information.manage.basic.audit.mapper.AuditLevelMapper;
import com.information.manage.basic.audit.mapper.AuditModelMapper;
import com.information.manage.basic.audit.mapper.AuditTaskMapper;
import com.information.manage.basic.audit.mapper.AuditUserMapper;
import com.information.manage.basic.audit.model.dto.AuditLevelDetailDto;
import com.information.manage.basic.audit.model.dto.AuditModelDetailDto;
import com.information.manage.basic.audit.model.entity.AuditLevelDO;
import com.information.manage.basic.audit.model.entity.AuditModelDO;
import com.information.manage.basic.audit.model.entity.AuditTaskDO;
import com.information.manage.basic.audit.model.entity.AuditUserDO;
import com.information.manage.basic.audit.service.AuditModelService;
import com.information.manage.basic.common.result.Result;
import com.information.manage.basic.common.token.TokenUtils;
import com.information.manage.basic.audit.model.query.AddAuditLevelQuery;
import com.information.manage.basic.audit.model.query.AddAuditModelQuery;
import com.information.manage.basic.audit.model.query.AuditModelQuery;
import com.information.manage.basic.audit.model.query.UpdateAuditModelQuery;
import com.information.manage.basic.common.utils.RedisUtils;
import com.information.manage.basic.mapper.SysRoleMapper;
import com.information.manage.basic.mapper.SysUserMapper;
import com.information.manage.basic.model.entity.SysRoleDO;
import com.information.manage.basic.model.entity.SysUserDO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
public class AuditModelServiceImpl extends ServiceImpl<AuditModelMapper, AuditModelDO> implements AuditModelService {

    @Autowired
    private SysRoleMapper sysRoleMapper;

    @Autowired
    private SysUserMapper sysUserMapper;

    @Autowired
    private AuditModelMapper auditModelMapper;

    @Autowired
    private AuditLevelMapper auditLevelMapper;

    @Autowired
    private AuditUserMapper auditUserMapper;

    @Autowired
    private AuditTaskMapper auditTaskMapper;

    @Autowired
    private RedisUtils redisUtils;

    private final static String redisCacheKey = "audit_model_used";


    @Override
    @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
    public Result createAuditTemplate(AddAuditModelQuery addAuditModelQuery, HttpServletRequest request) {
        Integer userId = TokenUtils.getUserId(StringUtils.isEmpty(request.getHeader("token")) ? request.getAttribute("token").toString() : request.getHeader("token"));
        Assert.isTrue(userId != null,"获取登陆用户信息为空");
        SysUserDO userDO = sysUserMapper.selectById(userId);
        // 验证参数
        validateAuditTemplate(addAuditModelQuery);

        AuditModelDO auditModelDO = this.buildAuditTemplate(addAuditModelQuery, userDO);
        this.auditModelMapper.insert(auditModelDO);

        // 保存审核层级
        saveAuditLevel(addAuditModelQuery.getAddAuditLevelQueryList(), auditModelDO.getId(), userDO);
        return Result.ok(auditModelDO);
    }

    /**
     * 存储审核层级及审核人
     */
    private void saveAuditLevel(List<AddAuditLevelQuery> addAuditLevelQueryList, Long modelId, SysUserDO userDO){
        // 保存审核层级
        Date date = new Date();
        for (AddAuditLevelQuery addAuditLevelQuery : addAuditLevelQueryList) {
            AuditLevelDO auditLevelDO = new AuditLevelDO();
            BeanUtils.copyProperties(addAuditLevelQuery, auditLevelDO);
            //设置审核模板ID
            auditLevelDO.setAuditModelId(modelId);
            //设置审核人类型 通过判断用户id及角色code确认
            List<String> auditUsers = addAuditLevelQuery.getAuditUsers();
            List<String> auditRoles = addAuditLevelQuery.getAuditRoles();
            StringBuilder shrlxSb = new StringBuilder();
            if (CollectionUtils.isNotEmpty(auditUsers)){ // 用户审核类型
                shrlxSb.append(ShrlxEnum.USER.name().toLowerCase());
            }
            if (CollectionUtils.isNotEmpty(auditRoles)){ // 角色审核类型
                if (shrlxSb.length() > 0){
                    shrlxSb.append(",");
                }
                shrlxSb.append(ShrlxEnum.ROLE.name().toLowerCase());
            }
            auditLevelDO.setType(shrlxSb.toString());
            auditLevelDO.setCreateUserId(userDO.getId().toString());
            auditLevelDO.setCreateUserName(userDO.getName());
            auditLevelDO.setCreateTime(date);
            auditLevelDO.setUpdateTime(date);
            this.auditLevelMapper.insert(auditLevelDO);

            //保存审核项_审核人员的映射关系
            List<AuditUserDO> mapAuditItemUsers = new LinkedList<>();
            if (CollectionUtils.isNotEmpty(auditUsers)){
                for (String userId: auditUsers) {
                    AuditUserDO auditUserDO = new AuditUserDO();
                    auditUserDO.setAuditLevelId(auditLevelDO.getId());
                    auditUserDO.setType(ShrlxEnum.USER.name().toLowerCase());
                    auditUserDO.setAuditObjectId(userId);
                    auditUserDO.setCreateUserId(userDO.getId().toString());
                    auditUserDO.setCreateUserName(userDO.getName());
                    auditUserDO.setCreateTime(date);
                    mapAuditItemUsers.add(auditUserDO);
                }
            }
            if (CollectionUtils.isNotEmpty(auditRoles)) {
                for (String roleId : auditRoles) {
                    AuditUserDO auditUserDO = new AuditUserDO();
                    auditUserDO.setAuditLevelId(auditLevelDO.getId());
                    auditUserDO.setType(ShrlxEnum.ROLE.name().toLowerCase());
                    auditUserDO.setAuditObjectId(roleId);
                    auditUserDO.setCreateUserId(userDO.getId().toString());
                    auditUserDO.setCreateUserName(userDO.getName());
                    auditUserDO.setCreateTime(date);
                    mapAuditItemUsers.add(auditUserDO);
                }
            }
            for (AuditUserDO mapAuditItemUser : mapAuditItemUsers) {  // 先这样用，后续批量插入
                this.auditUserMapper.insert(mapAuditItemUser);
            }
        }
    }

    private AuditModelDO buildAuditTemplate(AddAuditModelQuery addAuditModelQuery, SysUserDO userDO){
        AuditModelDO auditModelDO = new AuditModelDO();
        BeanUtils.copyProperties(addAuditModelQuery, auditModelDO);
        if (StringUtils.isEmpty(auditModelDO.getType())) auditModelDO.setType(AuditTypeEnum.OTHER.name());
        //新创建的审核模板默认设置为不启用
        auditModelDO.setUsed(0);
        //通过接口创建的都不是系统默认模板
        auditModelDO.setIsSystem(0);
        auditModelDO.setCreateUserName(userDO.getName());
        auditModelDO.setCreateUserId(userDO.getId().toString());
        Date date = new Date();
        auditModelDO.setCreateTime(date);
        auditModelDO.setUpdateTime(date);
        return auditModelDO;
    }


    private void validateAuditTemplate(AddAuditModelQuery addAuditModelQuery) {
        String type = addAuditModelQuery.getType();
        List<AddAuditLevelQuery> addAuditLevelQueryList = addAuditModelQuery.getAddAuditLevelQueryList();
        Assert.notEmpty(addAuditLevelQueryList, "审核层级不能为空");
        Assert.isTrue(addAuditLevelQueryList.stream().map(it -> it.getLevel()).distinct().collect(Collectors.toList()).size() == addAuditLevelQueryList.size(), "审核顺序必须唯一");

        for (AddAuditLevelQuery addAuditLevelQuery : addAuditLevelQueryList) {
            Assert.notNull(addAuditLevelQuery.getLevel(), "层级顺序不能为空");
            Assert.notNull(addAuditLevelQuery.getPassMethod(), "通过方式不能为空");
            List<String> auditUsers = addAuditLevelQuery.getAuditUsers();
            List<String> auditRoles = addAuditLevelQuery.getAuditRoles();
            Assert.isTrue(CollectionUtils.isNotEmpty(auditUsers) || CollectionUtils.isNotEmpty(auditRoles), "第" + addAuditLevelQuery.getLevel() + "层级请选择审核对象");
            //根据用户ID查询用户是否存在
            if (CollectionUtils.isNotEmpty(auditUsers)){  // 先这样用，后续批量检查
                for (String userId : auditUsers) {
                    SysUserDO userDO = sysUserMapper.selectById(userId);
                    Assert.notNull(userDO, "用户ID="+userId+"不存在");
                }
            }
            // 处理角色存在性
            if (CollectionUtils.isNotEmpty(auditRoles)){
                for (String roleId : auditRoles) {
                    SysRoleDO sysRoleDO = sysRoleMapper.selectById(roleId);
                    Assert.notNull(sysRoleDO, "角色CODE=" + roleId + "不存在");
                }
            }
        }
    }

    @Override
    public Result getAuditTemplatesPage(AuditModelQuery auditModelQuery) {
        Page<AuditModelDO> page = auditModelMapper.findPage(new Page(auditModelQuery.getCurPage(), auditModelQuery.getPageSize())
                , auditModelQuery.getAuditModelName(), auditModelQuery.getAuditModelType());
        return Result.ok(page);
    }

    @Override
    public Result<AuditModelDetailDto> getAuditTemplateById(Long id) {
        AuditModelDO auditModelDO = auditModelMapper.selectById(id);
        Assert.notNull(auditModelDO,"审核模板不存在，请刷新后再试");

        AuditModelDetailDto auditModelDetailDto = new AuditModelDetailDto();
        BeanUtils.copyProperties(auditModelDO, auditModelDetailDto);

        //查询模板下的层级列表
        List<AuditLevelDO> auditLevelDOList = this.auditLevelMapper.selectList(new LambdaQueryWrapper<AuditLevelDO>().eq(AuditLevelDO::getAuditModelId, auditModelDO.getId()));
        if(CollectionUtils.isNotEmpty(auditLevelDOList)){
            Map<String, SysUserDO> userNickNameMap = new HashMap<>();
            List<AuditLevelDetailDto> auditLevelDetailDtoList = new ArrayList<>();
            for (AuditLevelDO auditLevelDO : auditLevelDOList) {
                AuditLevelDetailDto auditLevelDetailDto = new AuditLevelDetailDto();
                BeanUtils.copyProperties(auditLevelDO, auditLevelDetailDto);
                //查询层级和审核人员的映射列表
                List<AuditUserDO> auditUserDOList = this.auditUserMapper.selectList(new LambdaQueryWrapper<AuditUserDO>().eq(AuditUserDO::getAuditLevelId, auditLevelDO.getId()));
                if(CollectionUtils.isNotEmpty(auditUserDOList)){
                    Map<String, String> userMap = new HashMap<>();
                    Map<String, String> roleMap = new HashMap<>();
                    for (AuditUserDO mapAuditItemUser: auditUserDOList) {
                        if (ShrlxEnum.USER.name().toLowerCase().equalsIgnoreCase(mapAuditItemUser.getType())){
                            String shrId = mapAuditItemUser.getAuditObjectId();
                            if(userNickNameMap.get(shrId) != null){
                                userMap.put(shrId, userNickNameMap.get(shrId).getName());
                            }else{
                                // 查询用户信息
                                SysUserDO userDO = sysUserMapper.selectById(Long.valueOf(shrId));
                                if(userDO != null){
                                    userMap.put(shrId, userDO.getName());
                                    //将查询的结果放入Map缓存
                                    userNickNameMap.put(shrId, userDO);
                                }else {
                                    log.info("未获取到用户id：{}的信息,审核层级中该用户信息为空", shrId);
                                }
                            }
                        }else if (ShrlxEnum.ROLE.name().toLowerCase().equalsIgnoreCase(mapAuditItemUser.getType())){
                            String shrId = mapAuditItemUser.getAuditObjectId();
                            SysRoleDO sysRoleDO = sysRoleMapper.selectById(shrId);
                            if (sysRoleDO != null) roleMap.put(shrId, sysRoleDO.getName());
                        }else {
                            Assert.isTrue(false, "不支持的审核人类型");
                        }
                    }
                    if (!userMap.isEmpty()) auditLevelDetailDto.setUserMap(userMap);
                    if (!roleMap.isEmpty()) auditLevelDetailDto.setRoleMap(roleMap);
                }
                auditLevelDetailDtoList.add(auditLevelDetailDto);
            }
            auditModelDetailDto.setAuditLevelDetailDtoList(auditLevelDetailDtoList);
        }

        return Result.ok(auditModelDetailDto);
    }

    @Override
    public Result updateAuditTemplate(HttpServletRequest request, UpdateAuditModelQuery updateAuditModelQuery) {
        Integer userId = TokenUtils.getUserId(StringUtils.isEmpty(request.getHeader("token")) ? request.getAttribute("token").toString() : request.getHeader("token"));
        Assert.isTrue(userId != null,"获取登陆用户信息为空");
        SysUserDO userDO = sysUserMapper.selectById(userId);

        //查询审核模板
        Long id = updateAuditModelQuery.getId();
        AuditModelDO auditModelDO = this.auditModelMapper.selectById(id);
        Assert.notNull(auditModelDO,"审核模板不存在，请刷新后再试");

        //应用中的审核模板不能修改
        Assert.isTrue(auditModelDO.getUsed() == AuditTemplateStatusEnum.STOP.getValue(), "应用中的审核流程不能修改");

        // 存在正在进行中的任务的模板不能被修改
        List<AuditTaskDO> auditTaskDOS = auditTaskMapper.getByModelIdAndState(id, AuditTaskStatusEnum.IN_PROCESS.getStatus(), 1);
        Assert.isTrue(CollectionUtils.isEmpty(auditTaskDOS), "该模板存在正在进行的任务");

        //添加更新审核流程的校验
        validateAuditTemplate(updateAuditModelQuery);

        BeanUtils.copyProperties(updateAuditModelQuery, auditModelDO);
        Date date = new Date();
        auditModelDO.setUpdateTime(date);
        this.auditModelMapper.updateById(auditModelDO);

        //删除旧的审核项和审核项_人员映射关系
        clearAuditItems(id);

        //保存审核层级
        saveAuditLevel(updateAuditModelQuery.getAddAuditLevelQueryList(), id, userDO);

        redisUtils.remove(redisCacheKey);
        //组装返回结果数据
        return Result.ok(updateAuditModelQuery);
    }

    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public Result applyAuditTemplate(Long id, HttpServletRequest request) {
        Integer userId = TokenUtils.getUserId(StringUtils.isEmpty(request.getHeader("token")) ? request.getAttribute("token").toString() : request.getHeader("token"));
        Assert.isTrue(userId != null,"获取登陆用户信息为空");
        SysUserDO userDO = sysUserMapper.selectById(userId);

        //查询审核模板
        AuditModelDO auditTemplate = this.auditModelMapper.selectById(id);
        Assert.notNull(auditTemplate,"审核模板不存在，请刷新后再试");


        //是否需要校验当前模板处于启用状态
        Assert.isTrue(auditTemplate.getUsed() == AuditTemplateStatusEnum.STOP.getValue(), "当前模板已经处于应用状态");

        //修改审核模板的应用状态
        auditTemplate.setUsed(AuditTemplateStatusEnum.APPLY.getValue());
        auditTemplate.setUpdateTime(new Date());
        this.auditModelMapper.updateById(auditTemplate);
        // 同时只能有一个应用的模板  先停用其它模板
        this.auditModelMapper.update(null, new LambdaUpdateWrapper<AuditModelDO>().set(AuditModelDO::getUsed, AuditTemplateStatusEnum.STOP.getValue())
                .notIn(AuditModelDO::getId, id));
        redisUtils.remove(redisCacheKey);
        return Result.ok(id);
    }

    @Override
    public Result stopAuditTemplate(Long id, HttpServletRequest request) {
        Integer userId = TokenUtils.getUserId(StringUtils.isEmpty(request.getHeader("token")) ? request.getAttribute("token").toString() : request.getHeader("token"));
        Assert.isTrue(userId != null,"获取登陆用户信息为空");
        SysUserDO userDO = sysUserMapper.selectById(userId);


        //查询审核模板
        AuditModelDO auditTemplate = this.auditModelMapper.selectById(id);
        Assert.notNull(auditTemplate,"审核模板不存在，请刷新后再试");


        //是否需要校验当前模板处于应用状态
        Assert.isTrue(auditTemplate.getUsed() == AuditTemplateStatusEnum.APPLY.getValue(), "当前模板已经处于停用状态");

        // 如果存在正在应用的流程 则不可以停用
        List<AuditTaskDO> shrws = auditTaskMapper.getByModelIdAndState(id, AuditTaskStatusEnum.IN_PROCESS.getStatus(), 1);
        Assert.isTrue(CollectionUtils.isEmpty(shrws), "该模板存在正在进行的任务");

        //修改审核模板的应用状态
        auditTemplate.setUsed(AuditTemplateStatusEnum.STOP.getValue());
        auditTemplate.setUpdateTime(new Date());
        this.auditModelMapper.updateById(auditTemplate);
        redisUtils.remove(redisCacheKey);

        return Result.ok(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public Result deleteAuditTemplate(Long id) {
        AuditModelDO auditModelDO = auditModelMapper.selectById(id);
        Assert.notNull(auditModelDO,"审核模板不存在，请刷新后再试");

        //应用中的审核模板不能删除
        Assert.isTrue(auditModelDO.getUsed() == AuditTemplateStatusEnum.STOP.getValue(), "应用中的审核流程不能删除");
        //删除模板之前需要查询与模板关联的模板层级数据
        clearAuditItems(auditModelDO.getId());

        //删除审核模板
        this.auditModelMapper.deleteById(id);
        redisUtils.remove(redisCacheKey);
        return Result.ok(id);
    }


    /**
     * 清除审核模板关联的层级以及层级和审核人员的映射
     * @param templateId
     */
    private void clearAuditItems(Long templateId) {
        //查询模板下的层级列表
        List<AuditLevelDO> auditLevelDOList = this.auditLevelMapper.selectList(new LambdaQueryWrapper<AuditLevelDO>().eq(AuditLevelDO::getAuditModelId, templateId));
        if(CollectionUtils.isNotEmpty(auditLevelDOList)){
            for (AuditLevelDO auditItem : auditLevelDOList) {
                //查询并删除层级和审核人员的映射列表
                List<AuditUserDO> auditUserDOList = this.auditUserMapper.selectList(new LambdaQueryWrapper<AuditUserDO>().eq(AuditUserDO::getAuditLevelId, auditItem.getId()));
                if(CollectionUtils.isNotEmpty(auditUserDOList)){
                    for (AuditUserDO shcjr : auditUserDOList) {
                        this.auditUserMapper.deleteById(shcjr.getId());
                    }
                }
                //删除层级
                this.auditLevelMapper.deleteById(auditItem.getId());
            }
        }
    }

    @Override
    public AuditModelDO readApplyAuditTemplate() {
        AuditModelDO auditModelDO = null;
        if (redisUtils.exists(redisCacheKey)){
            String redisCacheValue = redisUtils.get(redisCacheKey).toString();
            if (!StringUtils.isEmpty(redisCacheValue)){
                auditModelDO = JSONObject.parseObject(redisCacheValue, AuditModelDO.class);
                return auditModelDO;
            }
        }
        AuditModelDO modelDO = auditModelMapper.selectOne(new LambdaQueryWrapper<AuditModelDO>().eq(AuditModelDO::getUsed, AuditTemplateStatusEnum.APPLY.getValue()).last("limit 1"));
        if (modelDO != null){
            redisUtils.set(redisCacheKey, JSONObject.toJSONString(modelDO), 10 * 60L, TimeUnit.SECONDS);
            return modelDO;
        }
        return null;
    }
}
